using System;
using System.IO;
using System.Reflection;

// DONE: Arrays.Reverse to MSB machine is to omit.

namespace gds2text
{
	class MainClass
	{
		static string programPath = Assembly.GetEntryAssembly().Location;
		static string programName = System.Text.RegularExpressions.Regex.Replace(programPath,@"^.*[/\\]","");
		
		static byte[] filedata;
		static int pos=0;
		public static void Main(string[] args)
		{
			//Console.WriteLine("open file " + args[0]);
			
			if(args.Length!=1){
				Console.WriteLine("only 1 arguments need to run");
				Console.WriteLine("Usage: mono "+programName+" <filename>");
				Environment.Exit(-1);
			}
			
			if(!System.IO.File.Exists(args[0])){
				Console.WriteLine("Can't find file " + args[0]);
				Environment.Exit(-1);
			}
			
			FileInfo fileinfo=new FileInfo(args[0]);
			Console.WriteLine("filename : {0} (size {1} [bytes])",args[0],fileinfo.Length);
			   
			filedata = File.ReadAllBytes(args[0]);
			while(pos<fileinfo.Length){
				int datasize=buint2()-4;
				int record=buint2();
				int posreserve=pos;
				Console.Write(String.Format("Recordtype: {0,0:X4}H (length {1,1}) ",record,datasize));
				//
				switch(record){
				case 0x0000: // END OF FILE
					break;
				case 0x0002: // HEADER
					Console.WriteLine("HEADER: GDS Version = "+bsint2());
					break;
				case 0x0102: // BGNLIB
					Console.WriteLine("BGNLIB: mtime = "+bdatetime12() + ", atime = "+bdatetime12());
					break;
				case 0x0206: // LIBNAME
					Console.WriteLine("LIBNAME: library name = "+bchar(datasize));
					break;
				case 0x0305: // UNITS
					Console.WriteLine("UNITS: GRID UNIT = "+breal8()+ ", METER UNIT = "+breal8());
					break;
				case 0x0400: // ENDLIB
					Console.WriteLine("ENDLIB");
					break;
				case 0x0502: // BGNSTR
					Console.WriteLine("BGNSTR: mtime = "+bdatetime12() + ", atime = "+bdatetime12());
					break;
				case 0x0606: // STRNAME
					Console.WriteLine("STRNAME: structure name = "+bchar(datasize));
					break;
				case 0x0700: // ENDSTR
					Console.WriteLine("ENDSTR");
					break;
				case 0x0800: // BOUNDARY
					Console.WriteLine("BOUNDARY");
					break;
				case 0x0900: // PATH
					Console.WriteLine("PATH");
					break;
				case 0x0A00: // SREF
					Console.WriteLine("SREF");
					break;
				case 0x0B00: // AREF
					Console.WriteLine("AREF");
					break;
				case 0x0C00: // TEXT
					Console.WriteLine("TEXT");
					break;
				case 0x0D02: // LAYER
					Console.WriteLine("LAYER: layer number = "+buint2());
					break;
				case 0x0E02: // DATATYPE
					Console.WriteLine("DATATYPE: datatype = "+buint2());
					break;
				case 0x0F03: // WIDTH
					Console.WriteLine("WIDTH: width = "+bsint4());
					break;
				case 0x1003: // XY
					Console.WriteLine("XY: coordinates = "+XY(datasize));
					break;
				case 0x1100: // ENDEL
					Console.WriteLine("ENDEL");
					break;
				case 0x1206: // SNAME
					Console.WriteLine("SNAME: reference structure name = "+bchar(datasize));
					break;
				case 0x1302: // COLROW
					Console.WriteLine("COLRAW: columns = "+bsint2()+" , rows= "+bsint2());
					break;
				case 0x1400: // TEXTNODE
					Console.WriteLine("TEXTNODE (Not Used)");
					break;
				case 0x1500: // NODE
					Console.WriteLine("NODE");
					break;
				case 0x1602: // TEXTTYPE
					Console.WriteLine("TEXTTYPE: texttype = "+buint2());
					break;
				case 0x1701: // PRESENTATION
                    Console.WriteLine("PRESENTATION: Text presentation = "+presentation());
					break;
				case 0x1906: // STRING
                    Console.WriteLine("STRING: string = "+bchar(datasize));
					break;
               	case 0x1A01: // STRANS
                    Console.WriteLine("STRANS: Text Transformation = "+strans());
					break;
               	case 0x1B05: // MAG
                    Console.WriteLine("MAG: Magnification Factor =" + breal8());
					break;
               	case 0x1C05: // ANGLE
                    Console.WriteLine("ANGLE: Angular Rotation = " + breal8());
					break;
               	case 0x1F06: // REFLIBS
                    Console.WriteLine("REFLIBS: Reference Libraries = " + bchar(datasize));
					break;
				case 0x2006: // FONTS
                    Console.WriteLine("FONTS: Text Font = " + bchar(datasize));
					break;
               	case 0x2102: // PATHTYPE
                    Console.WriteLine("PATHTYPE: Path type = " + bsint2());
					break;
               	case 0x2202: // GENERATIONS
                    Console.WriteLine("GENERATIONS: Backup Copies = " + bsint2());
					break;
               	case 0x2306: // ATTRTABLE
                    Console.WriteLine("ATTRTABLE: Attribute definition file =" + bchar(datasize));
					break;
               	case 0x2406: // STYPTABLE
                    Console.WriteLine("STYPTABLE: Unreleased Feature =" + bchar(datasize));
					break;
               	case 0x2502: // STRTYPE
                    Console.WriteLine("STRTYPE: Unreleased Feature =" + bsint2());
					break;
               	case 0x2601: // ELFLAGS
                    Console.WriteLine("ELFLAGS: elflags = " + elflags());
					break;
               	case 0x2703: // ELKEY
                    Console.WriteLine("ELKEY: Unreleased Feature = " + bsint4());
					break;
               	case 0x2A02: // NODETYPE
                    Console.WriteLine("NODETYPE: Node Type = " + bsint2());
					break;
               	case 0x2B02: // PROPATTR
                    Console.WriteLine("PROPATTR: Attribute Number = " + bsint2());
					break;
               	case 0x2C06: // PROPVALUE
                    Console.WriteLine("PROPVALUE: Attribute Value = " + bchar(datasize));
					break;
               	case 0x2D00: // BOX
                    Console.WriteLine("BOX: Box Start");
					break;
               	case 0x2E02: // BOXTYPE
                    Console.WriteLine("BOXTYPE: Box Type = " + bsint2());
					break;
               	case 0x2F03: // PLEX
                    Console.WriteLine("PLEX: plex = " + bsint4());
					break;
               	case 0x3003: // BGNEXTN
                    Console.WriteLine("BGNEXTN: Number = " + bsint4());
					break;
               	case 0x3103: // ENDEXTN
                    Console.WriteLine("ENDEXTN: Number = " + bsint4());
					break;
               	case 0x3202: // TAPENUM
                    Console.WriteLine("Tape number = " + bsint2());
					break;
               	case 0x3302: // TAPECODE
                    Console.WriteLine("Tape code = " + bsint2());
					break;
               	case 0x3401: // STRCLASS
                    Console.WriteLine("Cadence Internal = " + strclass());
					break;
               	case 0x3503: // RESERVED
                    Console.WriteLine("RESERVED = " + bchar(datasize));
					break;
               	case 0x3602: // FORMAT
                    Console.WriteLine("FORMAT: number = " + bsint2());
					break;
               	case 0x3706: // MASK
                    Console.WriteLine("MASK: value = " + bchar(datasize));
					break;
               	case 0x3800: // ENDMASKS
                    Console.WriteLine("ENDMASKS: Mask End");
					break;
               	case 0x3902: // LIBDIRSIZE
                    Console.WriteLine("LIBDIRSIZE: Library Directory Size = " + bsint2());
					break;
               	case 0x3A06: // SRFNAME
                    Console.WriteLine("SRFNAME: Spacing Rules file =" + bchar(datasize));
					break;
               	case 0x3B02: // LIBSECUR
                    Console.WriteLine("LIBSECUR: Access Control List = " + libsecur(datasize));
					break;
               	case 0x3C00: // BORDER
                    Console.WriteLine("BORDER: Border Start");
					break;
               	case 0x3D00: // SOFTFENCE
                    Console.WriteLine("SOFTFENCE: Soft fence Start");
					break;
               	case 0x3E00: // HARDFENCE
                    Console.WriteLine("HARDFENCE: Hard fence Start");
					break;
               	case 0x3F00: // SOFTWIRE
                    Console.WriteLine("SOFTWIRE: Soft wire Start");
					break;
               	case 0x4000: // HARDWIRE
                    Console.WriteLine("HARDWIRE: Hard wire Start");
					break;
               	case 0x4100: // PATHPORT
                    Console.WriteLine("PATHPORT: Path port Start");
					break;
               	case 0x4200: // NODEPORT
                    Console.WriteLine("NODEPORT: Node port Start");
					break;
               	case 0x4300: // USERCONSTRAINT
                    Console.WriteLine("USERCONSTRAINT: User constraint Start");
					break;
               	case 0x4400: // SPACER ERROR
                    Console.WriteLine("SPACER_ERROR: Spacer error Start");
					break;
               	case 0x4500: // CONTACT
                    Console.WriteLine("CONTACT: Contact Start");
					break;
		        default:
					Console.WriteLine("undefined record (skip " + datasize + " bytes)");
					pos += datasize;
					break;
				}
				if(record==0x0000) break;
				pos=posreserve+datasize;
			}
			Console.WriteLine();
		}
		
		protected static string XY(int size){
			string retstr="";
			while(size>0){
				int x=bsint4();
				int y=bsint4();
				retstr = retstr + string.Format("({0,1},{1,1})",x,y);
				size -=8;
			}
			return(retstr);
		}
		protected static uint buint4(){
			// 4 byte unsigned integer
			byte[] bytes=new byte[4];
			for(int i=0;i<bytes.Length;++i){
				bytes[i]=filedata[pos++];
			}
			if(BitConverter.IsLittleEndian) Array.Reverse(bytes);
			return(BitConverter.ToUInt32(bytes,0));
		}
		protected static int buint2(){
			// 2 byte unsigned integer
			byte[] bytes=new byte[2];
			for(int i=0;i<bytes.Length;++i){
				bytes[i]=filedata[pos++];
			}
			if(BitConverter.IsLittleEndian) Array.Reverse(bytes);
			return(BitConverter.ToUInt16(bytes,0));
		}
		protected static int bsint4(){
			// 4 byte signed integer
			byte[] bytes=new byte[4];
			for(int i=0;i<bytes.Length;++i){
				bytes[i]=filedata[pos++];
			}
			if(BitConverter.IsLittleEndian) Array.Reverse(bytes);
			return(BitConverter.ToInt32(bytes,0));
		}
		protected static int bsint2(){
			// 2 byte signed integer
			byte[] bytes=new byte[2];
			for(int i=0;i<bytes.Length;++i){
				bytes[i]=filedata[pos++];
			}
			if(BitConverter.IsLittleEndian) Array.Reverse(bytes);
			return(BitConverter.ToInt16(bytes,0));
		}
		protected static string bdatetime12(){
			
			return (string.Format("{1,0:D2}/{2,0:D2}/{0,0:D4} {3,0:D2}:{4,0:D2}:{5,0:D2}",
			                      buint2()+1900,buint2(),buint2(),buint2(),buint2(),buint2()));
		}
		protected static string bchar(int size){
			byte[] bytes=new byte[size];
			for(int i=0;i<size;++i){
				bytes[i]=filedata[pos+i];
				if(bytes[i]<0x20 || bytes[i]>0x7f) bytes[i]=0x20;
			}
			pos += size;
			System.Text.Encoding enc=System.Text.Encoding.GetEncoding(0);
			return(enc.GetString(bytes));
		}
		protected static double breal8(){
			return(breal(8));
		}
		protected static double breal4(){
			return(breal(4));
		}
		
		protected static double breal(int size){
			int[] bytes=new int[size];
			for(int i=bytes.Length-1;i>=0;--i){
				bytes[i]=filedata[pos++];
			}
			int exponent =(bytes[bytes.Length-1] & 0x7F)-64;
			double retvalue=0.0;
			double dv=256.0;
			for(int i=bytes.Length-2;i>=0;--i){
				retvalue += ((double) bytes[i])/dv;
				dv*=256.0;
			}
			retvalue=retvalue*System.Math.Pow(16,exponent);
			return ((bytes[bytes.Length-1] & 0x80)==0 ? retvalue : -retvalue);
		}
		protected static string elflags(){
			int d0=filedata[pos++];
#pragma warning disable 0219
			int d1=filedata[pos++];
#pragma warning restore 0219
			string retstr="Template data is ";
			if((d0 & 0x80)==0){
				retstr += "off";
			}else{
				retstr += "on ";
			}
			retstr += ", External data is ";
			if((d0 & 0x40)==0){
				retstr += "off";
			}else{
				retstr += "on ";
			}
			return(retstr);
		}
		protected static string libsecur(int size){
			return(bchar(size));
		}
		protected static string presentation(){
#pragma warning disable 0219
			int d0=filedata[pos++];
#pragma warning restore 0219
			int d1=filedata[pos++];
			int font=(d1 & 12)/4;
			string retstr="Font " + font + ", VertJust ";
			int vert=(d1 & 48)/16;
			switch(vert){
			case 0:
				retstr += "Top";
				break;
			case 1:
				retstr += "Middle";
				break;
			default:
				retstr += "Bottom";
				break;
			}
			retstr += ", HorzJust ";
			int horiz=(d1 & 192)/64;
			switch(horiz){
			case 0:
				retstr += "Left";
				break;
			case 1:
				retstr += "Center";
				break;
			default:
				retstr += "Right";
				break;
			}
			return(retstr);				
		}
		protected static string strans(){
			int d0=filedata[pos++];
#pragma warning disable 0219
			int d1=filedata[pos++];
#pragma warning restore 0219
			string retstr=string.Format("0x{0,0:X2}{1,0:X2} ",d0,d1);
			retstr += ", X-Mirror: ";
			if((d0 & 0x80)==0){
				retstr += "off";
			}else{
				retstr += "on ";
			}
			retstr += ", Abs Mag: ";
			if((d0 & 0x20)==0){
				retstr += "off";
			}else{
				retstr += "on ";
			}
			retstr += ", Abs Angle: ";
			if((d0 & 0x40)==0){
				retstr += "off";
			}else{
				retstr += "on ";
			}
			return(retstr);
		}
		protected static string strclass(){
#pragma warning disable 0219
			int d0=filedata[pos++];
			int d1=filedata[pos++];
#pragma warning restore 0219
			return("Cadence internal use only");
		}
	}
}
